tree grammar JKlasyEmiter;

/*

  ======================================================================
      type   | allowed operators

     integer | = != < <= > >= + - * div mod
        real | = != < <= > >= + - * div
      string | = !=           +
        char | = !=
   reference | = != < <= > >= + - * div mod
     boolean | = != and not or
      arrays | 

  
  ======================================================================
    operator -> translates to function
    and -> __and__
    or -> __or__
		=  -> __equal__
		!= -> __notequal__
		<  -> __less__
		<= -> __lessequal__
		>  -> __greater__
		>= -> __greaterequal__
		+  -> __plus__
		-  -> __minus__
		*  -> __multiply__
		div -> __divide__
		mod -> __modulo__
		
	======================================================================
*/


options {
	tokenVocab=JKlasy;
	ASTLabelType=Tree;
	output=template;
}

@header {
package compiler.emiter;

import compiler.emiter.expressiontypes.*;

import java.io.FileWriter;
import java.io.IOException;
}

@members {

private static final String OP_AND_NAME     = "__and__";
private static final String OP_OR_NAME      = "__or__";
private static final String OP_EQUAL_NAME   = "__equal__";
private static final String OP_NOTEQ_NAME   = "__notequal__";
private static final String OP_GREATER_NAME = "__greater__";
private static final String OP_GREATER_EQUAL_NAME = "__greaterequal__";
private static final String OP_LESS_NAME    = "__less__";
private static final String OP_LESS_EQUAL_NAME = "__lessequal__";
private static final String OP_PLUS_NAME    = "__plus__";
private static final String OP_MINUS_NAME   = "__minus__";
private static final String OP_MULT_NAME 	= "__multiply__";
private static final String OP_DIV_NAME     = "__divide__";
private static final String OP_MOD_NAME     = "__modulo__";

}

program [EmiterEnv environment, String main_file] 
	returns [ArrayList<String> createdFiles] throws IOException
	@init {
	
	  ArrayList<String> createdFiles = 
	    new ArrayList<String>();
	  
	  final String mainClassFilename = 
	    main_file;
	    
	}	
	
	@after {
	  
	  retval.createdFiles = createdFiles;
	  
	} : 
	    ^(PROGRAM {
		   FileWriter writer = null;
		} mp=main_part[environment, main_file] {
		  
		  try {
		   writer = 
		     new FileWriter(mainClassFilename + ".j", false);
		   
		   writer.write(mp.st.toString());
		   writer.close();
		   
		   // developing purposes only
		   createdFiles.add(mainClassFilename + ".j");   
		   
		   // developing purposes only
	       // System.out.println(mp.st.toString());
		  } catch (IOException e) {
		   // what to do? exception passing does not work!
		   
		   e.printStackTrace();
		  }
		
		}(cd = class_definition[environment] {
		  
		  try {
		   writer = 
		     new FileWriter(cd.className + ".j", false);
		     
		   writer.write(cd.st.toString());
		   writer.close();
		   
		   createdFiles.add(cd.className + ".j");
		   
		   // developing purposes only
		   // System.out.println(cd.st.toString());
		   
		  } catch (IOException e) {
		    // what to do? exception passing does not work!
	      
	      e.printStackTrace();
		  } 
		})*) 
		;

main_part [EmiterEnv environment, String className] 
	: 
	  { 
	    VariableEnumerator variableEnumerator = new VariableEnumerator(); 
	  }
	
	  ^(MAINPART 
	     (
	     	vd=variable_declaration
		    {
		  	 	//we have to count local variables also
	            for (int i = 0; i < vd.variables.size(); i++) {
	              variableEnumerator.addVariable(vd.variables.get(i).name, 
	                 vd.variables.get(i).type, false);
	            }
	            environment.variableEnumerator = variableEnumerator;
            }
	     )*
	     stmts = statements[environment])
	     {
	     	if(vd == null) {
	     		vd = new variable_declaration_return();
	     		vd.variables = new ArrayList<Variable>();
	     	}
	     	environment.arraysCode();
	     } 
	
	-> main_part_template(
		 name = {className},
	     statements = {$stmts.st},
	     arrays_init = {environment.localArraysInit},
	     max_stack = {$stmts.maxStack},
	     max_locals = {Integer.toString(1 + environment.varSize())})
	;
	
class_definition [EmiterEnv environment] returns [String className]
	: 
	{ 
      environment.variableEnumerator = new VariableEnumerator();
      ArrayList<FunctionType> methodHeaders = null;
      String stdcons = "";
    }
	
	 ^(CLASSDEFINITION id = ID 
		 (
		  
		   { 
		    methodHeaders = new ArrayList<FunctionType>(); 
      		environment.currentClass = $id.text;
		   }
		   
		   (od = operation_descriptor { methodHeaders.add($od.type); })* 
		   fd = fields_declaration[environment]
	   	   (
	   	   	  { 
	   	   	  	environment.variableEnumerator = 
	   	   	  		new VariableEnumerator(fd.variableEnum); 
	   	   	  }
	   	 	  oa += operation_algorithm[environment, methodHeaders]
	   	   )*
		 )
	   )
		
       {
  		  if(!environment.hasStdConsImpl($id.text)) {
  			environment.arraysCode();
  			stdcons = ".method public <init>()V\n" +
  					  "   aload_0\n" +
  					  "   invokenonvirtual java/lang/Object/<init>()V\n" +
  					  "   return\n" +
  					  environment.consArraysInit + "\n" + 
  					  ".end method";
  		  }
      	  methodHeaders = null;
		  retval.className = $id.text; 
       }
		
		-> class_definition_template(
		    name={$id.text},
		    stdCons={stdcons}, 
		    fields = {$fd.st},
		    methods = {$oa})
	;
	
fields_declaration[EmiterEnv environment]
	returns[VariableEnumerator variableEnum]
	@init {
	  String code = "";
	  retval.variableEnum = new VariableEnumerator();
	}
	
	@after {
	  retval.st = new StringTemplate(code);
	} :
	   (vars = variable_declaration
	    { 
	      for (Variable var : vars.variables) {
	      	  retval.variableEnum.addVariable(var.name, var.type, true);
		      code += ".field private " + var.name + " ";
		      if (var.type.isArrayType)
		      	code += "[";
		      	
		      if (var.type instanceof IntegerType) {
		        code += "I";
		      }
		      else if (var.type instanceof RealType) {
		        code += "F";
		      }
		      else if (var.type instanceof BooleanType) {
		        code += "Z";
		      }
		      else if (var.type instanceof CharacterType) {
		        code += "C";
		      } 
		      else if (var.type instanceof StringType) {
		        code += "Ljava/lang/String";
		      } 
		      else if (var.type instanceof ReferenceType) {
		        code += "L" + ((ReferenceType)var.type).name + ";";
		      } 
		      else {
		        System.err.println("error? " + var.type);
		      }
		      
		      code += "\n";
		    }
		  }    
	    )*
		;
	
variable_declaration returns [ArrayList<Variable> variables]
	@init {
	  retval.variables = new ArrayList<Variable>(); 
	}
	  
		: ^(VARIABLEDECLARATION 
		     (dt = declaration_type id=ID 
		       { retval.variables.add(new Variable($id.text, $dt.type, 0, true)); } )+
		    )		
		;

declaration_type returns [Type type]
	: 
	{ boolean arrayType = false; }
	
	^(DECLARATIONTYPE st = simple_type 
	        (^(ARRAY OF size=INTNUMBER) { arrayType = true; } )? )
	        
	{ 
	  if(arrayType) {
	    if ($st.type instanceof IntegerType) {
	      retval.type = new IntegerType(true, Integer.parseInt($size.getText()));
	    } 
	    else if ($st.type instanceof RealType) {
	      retval.type = new RealType(true, Integer.parseInt($size.getText()));
	    } 
	    else if ($st.type instanceof CharacterType) {
	      retval.type = new CharacterType(true, Integer.parseInt($size.getText()));
	    }
	    else if ($st.type instanceof StringType) {
	      retval.type = new StringType(true, Integer.parseInt($size.getText()));
	    }
	    else if ($st.type instanceof BooleanType) {
	      retval.type = new BooleanType(true, Integer.parseInt($size.getText()));
	    } 
	    else if ($st.type instanceof ReferenceType) {
	      retval.type = new ReferenceType(
	       ((ReferenceType)($st.type)).name, true, Integer.parseInt($size.getText()));
	    }
	    else {
		   /* Disabled due to the fact, that antlr 3.0 can not handle 
		      exception passing. Printing information to stderr instead.
		    
		      throw new JKlasyEmitterException(
		       "Invalid or unknown type in declaration: {0}\n", $st.type);
		   */	     
		   System.err.println("Invalid or unknown type in declaration: " 
		      + $st.type);
	    }	  
	  } else {
	    retval.type = $st.type;
	  }
	} 
	;

parameter_type returns [Type type]
	: 
	 { boolean arrayType = false; }
	
	^(PARAMETERTYPE 
		st = simple_type
		(^(ARRAY OF) { arrayType = true; } )?
	  ) 
	  
	   { 
    if(arrayType) {
      if ($st.type instanceof IntegerType) {
        retval.type = new IntegerType(true, 0);
      } 
      else if ($st.type instanceof RealType) {
        retval.type = new RealType(true, 0);
      } 
      else if ($st.type instanceof CharacterType) {
        retval.type = new CharacterType(true, 0);
      }
      else if ($st.type instanceof StringType) {
        retval.type = new StringType(true, 0);
      }
      else if ($st.type instanceof BooleanType) {
        retval.type = new BooleanType(true, 0);
      } 
      else if ($st.type instanceof ReferenceType) {
        retval.type = new ReferenceType(
          ((ReferenceType)(st.type)).name, true, 0);
      }
      else {      	
	   System.err.println(
	    "Invalid or unknown type in declaration: " +  $st.type);
      }
      /* Disabled due to the fact, that antlr 3.0 can not handle exception passing. 
         Printing information to stderr instead.
       
         throw new JKlasyEmitterException(
          "Invalid or unknown type in declaration: {0}\n", $st.type);
       */
        
    } else {
      retval.type = $st.type;
    }
  } 
	;
	
simple_type returns [Type type]
	: INTT    { retval.type = new IntegerType(false, 0);   }
	| BOOLT   { retval.type = new BooleanType(false, 0);   }
	| STRINGT { retval.type = new StringType(false, 0);    }
	| REALT   { retval.type = new RealType(false, 0);     }
	| CHART   { retval.type = new CharacterType(false, 0); }
	| ID      { retval.type = new ReferenceType($ID.getText(), false, 0); }
	;
	
return_type returns [Type type]
	: 
	  { boolean parameterPresent = false; }
	  ^(RESULT (pt = parameter_type { parameterPresent = true; })?)
	  
	  { retval.type = parameterPresent? pt.type : null; } 
	;

modifier returns [String modifier]
	: PRIVATE { retval.modifier = FunctionType.PRIVATE_MOD; }
	| PUBLIC { retval.modifier = FunctionType.PUBLIC_MOD; }
	;
	
operation_descriptor returns [FunctionType type]
	@init {
	 ArrayList<Type> argumentTypes = new ArrayList<Type>();
	 
	 String mod = FunctionType.PRIVATE_MOD;
	 
	} : ^(OPERATIONDESCRIPTOR (m = modifier { mod = $m.modifier; })?  
			 ds = designator
			(pt = parameter_type { argumentTypes.add(pt.type); })* 		
	     rt = return_type)
		  
		  { 
		   retval.type = new FunctionType(mod, ds.name, 
		     argumentTypes, rt.type); 
		  }
		;
	
designator returns [String name]
  : ID { retval.name = $ID.text; }
  | ro = relational_operator { retval.name = ro.name; }
  | ao = additive_operator { retval.name = ao.name; }
  | mo = multiplicative_operator { retval.name = mo.name; }
  ;

operation_algorithm[EmiterEnv environment, ArrayList<FunctionType> declaredFunctions] 
  returns [String name, ArrayList<String> argNames]
	@init {
	  ArrayList<String> argNames = new ArrayList<String>(); 
	  String argTypesString = "";
	  String retTypeString = "V";
	  String modString = FunctionType.PRIVATE_MOD;
	  String rettyp = "";
	  String newret = "";
	  String name = "";
	  String cons = "";
	  String arraysInit = "";
	}  
		: ^(OPERATIONALGORITHM 
		     ds = designator 
		     
		     ^(ARGUMENTS (((ID { argNames.add($ID.text); })+)?)) 
		 
		    {
		    	environment.currentFun = ds.name;
		    	if(ds.name.equals(environment.currentClass)) {
		    		name = "<init>";
		    		cons = "\naload_0\n" +
						   "invokenonvirtual java/lang/Object/<init>()V";
		    	}
		    	else {
		    		name = ds.name;
		    	}   
		        for (FunctionType function : declaredFunctions) {   
		           if (function.name.equals(ds.name) && 
		              function.argTypes.size() == argNames.size()) {
		      
		           // we have found the appropriate function header, 
		           // so we can generate function argument declarations 
		           // in the method body
		         
		           // generate method header (argument part)
		           for (Type type : function.argTypes) {
		             argTypesString +=
		                 type instanceof StringType ? "Ljava/lang/String;" :
		                 type instanceof IntegerType ? "I" :
		                 type instanceof RealType ? "F" :
		                 type instanceof BooleanType ? "Z" :
		                 type instanceof CharacterType ? "C" :
		                 type instanceof ReferenceType ? 
		                   "L" + ((ReferenceType)type).name + ";" : "V";
		               
		               
		             if (type.isArrayType){
		               argTypesString = "[" + argTypesString;
		             
		             modString = function.mod;
		             }
		           }
		         
		           // determing the return type
		           retTypeString = 
		             function.retType == null? "V" :
		             function.retType instanceof StringType ? "Ljava/lang/String;" :
		             function.retType instanceof IntegerType ? "I" :
		             function.retType instanceof RealType ? "F" :
		             function.retType instanceof CharacterType ? "C" :
		             function.retType instanceof BooleanType ? "Z" :
		             function.retType instanceof ReferenceType ? 
		               "L" + ((ReferenceType)function.retType).name + ";" : "V";
		               
		           if(function.retType != null && function.retType.isArrayType)
		          	 retTypeString = "[" + retTypeString;
		         	     
		           // we have to count argument variables
		           for (int i = 0; i < argNames.size(); i++) {
		             environment.variableEnumerator.addVariable(argNames.get(i), 
		                function.argTypes.get(i), false);
		           }	     
		             
		           // that's it!
		           break;
		         }
		       }
		     }
		     
		  	 ^(VARIABLES 
		  	 	(vd = variable_declaration
		  	 	{
			  	 	//we have to count local variables also
		            for (int i = 0; i < vd.variables.size(); i++) {
		              environment.variableEnumerator.addVariable(vd.variables.get(i).name, 
		                 vd.variables.get(i).type, false);
		            }	     
		  	 	} 
		  	 	)*) 
		  	   st = statements[environment]
		  	   {
		  	   	  if( vd == null) {
		  	   	  	vd = new variable_declaration_return();
		  	   	  	vd.variables = new ArrayList<Variable>();
		  	   	  }					  
				  environment.arraysCode();
				  if(ds.name.equals(environment.currentClass))
					arraysInit = environment.consArraysInit;
				  else 
					arraysInit = environment.localArraysInit;
		  	   }
		  	 (^(RETURN ret = expression[environment])
		  	 {
		  	 	newret = $ret.st.toString();
		  	 	rettyp = $ret.type == null ? "a" :
		  	 		 $ret.type.isArrayType ? "a" :
		             $ret.type instanceof StringType ? "a" :
		             $ret.type instanceof IntegerType ? "i" :
		             $ret.type instanceof RealType ? "f" :
		             $ret.type instanceof CharacterType ? "i" :
		             $ret.type instanceof BooleanType ? "i" :
		             $ret.type instanceof ReferenceType ? "a" : "a";
		  	 }		  	 
		  	 )? )
		  	 
		    -> method_template(
				  arrays_init = {arraysInit},
		          varnumber = {Integer.toString(1 + environment.varSize()) },
		          cons = {cons},
	              mod = {modString}, 
	              name = {name}, 
	              argTypes = {argTypesString}, 
	              retType = {retTypeString},
	              statements = {$st.st},
	              returnExpr = {newret},
	              returnType = {rettyp})
		;

statements[EmiterEnv environment] 
  returns [int maxStack, int maxLocals]  
	: 
	 { int maxStack = 2; int maxLocals = 2;}
	 
	   ^(STATEMENTS 
	     (stmts += statement[environment])*) 
	   
	 { retval.maxStack = maxStack; retval.maxLocals = maxStack; }
	 
	-> statements_template(statements = {$stmts})
	;

statement[EmiterEnv environment]
  
  : stmt1 = assign_statement[environment]
     -> statement_template(statement = {$stmt1.st})
	
	| stmt2 = if_statement[environment] 
	   -> statement_template(statement = {$stmt2.st})
	   
	| stmt3 = while_statement[environment]
	   -> statement_template(statement = {$stmt3.st})
	   
	| stmt4 = input_statement[environment]
	   -> statement_template(statement = {$stmt4.st})
	   
	| stmt5 = output_statement[environment]
	   -> statement_template(statement = {$stmt5.st})
	
	| stmt6 = call_statement[environment]
	   -> statement_template(statement = {$stmt6.st})
	;

if_statement [EmiterEnv environment]
  
	: ^(IFSTATEMENT 
	       e = expression[environment] 
	       c1 = conditional_option[environment]
	       (
	       	c2 = conditional_option[environment]  -> 
	       		ifelse_statement_template(
			     label1 = { LabelGenerator.nextLabel() },
			     label2 = { LabelGenerator.nextLabel() },
			     expression = {$e.st}, 
			     conditional1 = {$c1.st},
			     conditional2 = {$c2.st})
			|() -> 
				if_statement_template(
			     label = { LabelGenerator.nextLabel() },
			     expression = {$e.st}, 
			     conditional = {$c1.st})
	       	)
	     )
	;
	
conditional_option[EmiterEnv environment]

	: 
	  s = statement[environment] 
	     -> conditional_option_template(raw = {$s.st})
	     
	| ss = statements[environment] 
	     -> conditional_option_template(raw = {$ss.st})
	;
	
assign_statement[EmiterEnv environment]

	: ^(ASSIGNSTATEMENT  
	     id = extended_id
	     {
	     	String varname = id.quantifiers.size() == 0 ? id.core : 
	     					 	id.quantifiers.get(0);
	     	boolean isArr = false;
	     		     		
	     	Variable var = environment.variableEnumerator.getVariable(varname);
	     	
	     	String type1 = "";
	     	String type2 = "";
	     	String code = "";	     	
	     }
	     (INDEX ei = expression[environment] {isArr = true;} )?
	     ex = expression[environment])
	     {	     	
	     	type1 = var.type == null? "a" :
	     			var.type.isArrayType && !isArr ? "a" :
		            var.type instanceof StringType ? "a" :
		            var.type instanceof IntegerType ? "i" :
		            var.type instanceof RealType ? "f" :
		            var.type instanceof CharacterType ? "i" :
		            var.type instanceof BooleanType ? "i" :
		            var.type instanceof ReferenceType ? "a" : "a";
		            
	     	type2 = var.type == null? "V" :
		            var.type instanceof StringType ? "Ljava/lang/String" :
		            var.type instanceof IntegerType ? "I" :
		            var.type instanceof RealType ? "F" :
		            var.type instanceof CharacterType ? "C" :
		            var.type instanceof BooleanType ? "Z" :
		            var.type instanceof ReferenceType ? 
		            	"L" + ((ReferenceType)var.type).name + ";" : "V";
		    
		    if(var.type.isArrayType)
		    	type2 = "[" + type2;
		    // object field
		    if(var.isField) {
				code = "aload_0\n";			  // get (this)
				// we have tab[i] assigment
				if(var.type.isArrayType && isArr) {
					//load array ref
					code += "getfield " + environment.currentClass + 
							"/" +	var.name + " " + type2 + "\n";
					code += $ei.st + "\n";		// load array index
					code += $ex.st + "\n";		// load value to store
					code += type1 + "astore\n"; // store it 
				}
				else {
					code += $ex.st + "\n";		// load value
					// store it in field
					code += "putfield " + environment.currentClass + 
							"/" + var.name + " " + type2 + "\n";
				}
	     	}
	     	// local variable
	     	else {
	     		// writing to table under specified index
	     		if(var.type.isArrayType && isArr) {		
	     			code += "aload " + var.number + "\n";	// load array ref
	     			code += $ei.st	+ "\n";					// calculate index
	     			code += $ex.st  + "\n";					// calculate value
	     			code += "\n" + type1 + "astore\n";		// store it!
	     		}
	     		// just writing to variable (maybe referenve)
	     		else {
	     			code += $ex.st;
	     			code += "\n" + type1 + "store " + var.number + "\n";
	     		}     		
	     	}
	     }
	
	-> assign_statement_template(code = {code})
	;
	
call_statement[EmiterEnv environment]

	: ^(CALLSTATEMENT ce=call[environment])
		{
			String pop = "";
			if(ce.type != null)
				pop = "pop\n";
		}
	-> call_statement_template(cexpr={$ce.st}, pop={pop})
	;

call[EmiterEnv environment]
		returns [Type type]
	@init {
	  ArrayList<String> arguments = new ArrayList<String>();
	  String callCode = "";
	  String prolog = "";
	  String afterExpr = "";
	}	: ^(CALL 
		     ei = extended_id
		     ( ex += expression[environment] )*)
	       
	       {
	       		if ($ex == null) 
	       			$ex = new ArrayList();
	       		String core = $ei.core;
	       		List<String> quant = new ArrayList<String>($ei.quantifiers);
	       		
	       		if(quant.size() == 0) {					// call method()
	       			if(environment.isDefined(core)) {	// calling constructor()
	       				prolog = "new " + core;
	       				prolog += "\ndup\n";
	       				callCode = "invokespecial " + core + "/" +
	       					environment.getFunSig(core, core, $ex.size());
	       				retval.type = new ReferenceType(core, false, 0);
	       			}	
	       			else if(environment.hasGlobalFun(core)) {
	       				afterExpr = "\nf2d";
	       				callCode = "invokestatic java/lang/Math/" +
	       						environment.getGlobalFunSig(core);
	       				callCode += "\nd2f\n";
	       				List tmp = new ArrayList();
	       				for(int i = 0; i < $ex.size(); i++) {
	       					tmp.add($ex.get(i));
	       					tmp.add(afterExpr);
	       				}
	       				$ex = tmp;
	       			}
	       			else {								// calling normal method
	       				prolog = "aload 0\n";
	       				callCode = "invokevirtual " + environment.currentClass + "/" + 
	       					environment.getFunSig(environment.currentClass, 
	       										core, $ex.size()) + "\n";
	       					
	       				String rett = environment.getFunRetType(environment.currentClass, core, $ex.size());
	       				boolean isArray = rett.startsWith("[") ? true : false;
	       				rett = rett.startsWith("[") ? rett.substring(1, rett.length()-1) : rett;
	       				retval.type = rett == "#void" ? null :
	       							  rett == "integer" ? new IntegerType(isArray, 0) :
	       							  rett == "real" ? new RealType(isArray, 0) :
	       							  rett == "char" ? new CharacterType(isArray, 0) :
	       							  rett == "boolean" ? new BooleanType(isArray, 0) :
	       							  rett == "string" ? new StringType(isArray, 0) :
	       							  		new ReferenceType(rett, isArray, 0);
	       			}
	       		}
	       		else {									// call object.method()
       				Variable var = 
       					environment.variableEnumerator.getVariable(quant.get(0));
       				if(var.isField) {					// object is a field
       					String type = var.type == null? "V" :
		            			var.type instanceof StringType ? "Ljava/lang/String" :
		            			var.type instanceof IntegerType ? "I" :
		            			var.type instanceof RealType ? "F" :
		            			var.type instanceof CharacterType ? "C" :
		            			var.type instanceof BooleanType ? "Z" :
		            			var.type instanceof ReferenceType ? 
		            					"L" + ((ReferenceType)var.type).name + ";" : "V";
		            					
       					prolog += "aload 0\n";			// load this
       					prolog += "getfield " + environment.currentClass + 
							"/" +	var.name + " " + type + "\n";
       				}
       				else {	       					       					
	       				String prefix = var.type == null? "a" :
						            var.type instanceof StringType ? "a" :
						            var.type instanceof IntegerType ? "i" :
						            var.type instanceof RealType ? "f" :
						            var.type instanceof CharacterType ? "i" :
						            var.type instanceof BooleanType ? "i" :
						            var.type instanceof ReferenceType ? "a" : "a";
	       				prolog = prefix + "load " + var.number + "\n";
       				}
       				ReferenceType t = (ReferenceType)var.type;
       				callCode = "invokevirtual " + t.name +	"/" + 
       					environment.getFunSig(t.name, core, $ex.size()) + "\n";
       					
       				String rett = environment.getFunRetType(t.name, core, $ex.size());
       				boolean isArray = rett.startsWith("[") ? true : false;
       				rett = rett.startsWith("[") ? rett.substring(1, rett.length()-1) : rett;
       				retval.type = rett == "#void" ? null :
       							  rett == "integer" ? new IntegerType(isArray, 0) :
       							  rett == "real" ? new RealType(isArray, 0) :
       							  rett == "char" ? new CharacterType(isArray, 0) :
       							  rett == "boolean" ? new BooleanType(isArray, 0) :
       							  rett == "string" ? new StringType(isArray, 0) :
       							  		new ReferenceType(rett, isArray, 0);
	       		}
	       }
	    -> call_expression_template(prolog={prolog}, exprs={$ex}, code={callCode})
		;
	
while_statement [EmiterEnv environment]
	: ^(WHILESTATEMENT 
	       e = expression[environment]
         stmts = statements[environment])
	
	-> while_statement_template(
	     label1 = { LabelGenerator.nextLabel() },
	     label2 = { LabelGenerator.nextLabel() },
	     expression = {$e.st}, 
	     statements = {$stmts.st})
	;

extended_id 
  returns [String core, ArrayList<String> quantifiers]
	@init {
	  ArrayList<String> quantifiers = new ArrayList<String>();
	  String core = "";
	}
	@after {
	  retval.core = core;
	  retval.quantifiers = quantifiers;
	}
		:   
			^(EXTENDEDID (ID { quantifiers.add($ID.text); })+)
			
			{ 
			  core = quantifiers.get(quantifiers.size() - 1); 
			  quantifiers.remove(quantifiers.size() - 1);
			} 
		;  
  
input_statement [EmiterEnv environment]
	@init {
	  String code = "";
	}

	@after {
	  retval.st = new StringTemplate(code);
	}
		: ^(INPUTSTATEMENT 
			(
				ei = extended_id
				{
					String varname = $ei.quantifiers.size() == 0 ?
							$ei.core : $ei.quantifiers.get(0);
	     			Variable var = environment.variableEnumerator.getVariable(varname);
	     			
	     			if(var.isField) {
	     				code += "aload_0\n";
	     			}
	     			code += "   new java/util/Scanner\n   dup\n";
	     			code += "   getstatic java/lang/System.in Ljava/io/InputStream;\n";
	     			code += "   invokespecial java/util/Scanner/<init>(Ljava/io/InputStream;)V\n";
	     			
	     			String prefix;
	     			String fldtype;
	     			if(var.type instanceof IntegerType) {
	     				prefix = "i";
	     				fldtype = "I";
	     				code += "   invokevirtual java/util/Scanner/nextLine()Ljava/lang/String;\n";
	     				code += "   invokestatic java/lang/Integer/parseInt(Ljava/lang/String;)I\n";
	     			}
	     			else if(var.type instanceof RealType) {
	     				prefix = "f";
	     				fldtype = "F";
	     				code += "   invokevirtual java/util/Scanner/nextLine()Ljava/lang/String;\n";
	     				code += "   invokestatic java/lang/Float/parseFloat(Ljava/lang/String;)F\n";
	     			}
	     			else {
	     				prefix = "a";
	     				fldtype = "Ljava/lang/String;";
	     				code += "   invokevirtual java/util/Scanner/nextLine()Ljava/lang/String;\n";
	     			}
	     			
	     			if(var.isField) {
	     				code += "   putfield " + environment.currentClass + "/" +
	     					var.name + fldtype + "\n";
	     			}
	     			else {
	     				code += "   " + prefix + "store " + var.number + "\n";
	     			}
	     			code += "\n";
				}
			)+
		  )	
		-> input_statement_template(code={code})
		;
				
output_statement [EmiterEnv environment]
	@init {
	  String code = "";
	}
	
	@after {
	  retval.st = new StringTemplate(code);
	}
		: ^(OUTPUTSTATEMENT 
		     (ex = expression[environment]
		{   
		    if ($ex.type instanceof IntegerType) {
	        code += "\ngetstatic java/lang/System/out Ljava/io/PrintStream;\n" +
	          $ex.st + "\ninvokevirtual java/io/PrintStream/print(I)V\n";
	      } 
	      if ($ex.type instanceof RealType) {
	        code += "\ngetstatic java/lang/System/out Ljava/io/PrintStream;\n" +
	          $ex.st + "\ninvokevirtual java/io/PrintStream/print(F)V\n";
	      } 
	      if ($ex.type instanceof CharacterType) {
	        code += "\ngetstatic java/lang/System/out Ljava/io/PrintStream;\n" +
	          $ex.st + "\ninvokevirtual java/io/PrintStream/print(C)V\n";
	      }
	      if ($ex.type instanceof StringType) {
	        code += "\ngetstatic java/lang/System/out Ljava/io/PrintStream;\n" +
	          $ex.st + "\ninvokevirtual java/io/PrintStream/print(Ljava/lang/String;)V";
	      }
	      if ($ex.type instanceof BooleanType) {
	        code += "\ngetstatic java/lang/System/out Ljava/io/PrintStream;\n" +
	          $ex.st + "\ninvokevirtual java/io/PrintStream/print(Z)V";
	      } 
	      if ($ex.type instanceof ReferenceType) {
	      
	      }
		}     
		     )+)
		     
		{ 
		  code += 
		    "\ngetstatic java/lang/System/out Ljava/io/PrintStream;\n" +
	      	"invokevirtual java/io/PrintStream/println()V\n";
	  }
		;
	
expression [EmiterEnv environment] 
  returns [Type type]
	@after {
	  retval.type = $e.type; 
	}
		: ^(EXPRESSION e = expr[environment])
		 
		-> expression_template(expression = {$e.st})
		;

expr [EmiterEnv environment] returns [Type type] 
  
	: ^(EQUAL 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = new BooleanType(false,0); }
	   
		( 
		     { e1.type instanceof IntegerType }? -> 
		       integer_equal_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
		         expression1 = {$e1.st}, 
		         expression2 = {$e2.st})
		|
		     { e1.type instanceof RealType }? ->
		       float_equal_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})  
	    | 
	       { e1.type instanceof ReferenceType }? ->
	         reference_equal_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})         
	    | 
	       { e1.type instanceof BooleanType }? ->
	         boolean_equal_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})         
	    | 
	       { e1.type instanceof CharacterType }? ->
	         character_equal_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st}) 
	    | 
	       { e1.type instanceof StringType }? ->
	         string_equal_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})   
	    )
	
	| ^(NOTEQ 
	     e1 = expr[environment] 
	     e2 = expr[environment]) 
	     
	   { retval.type = new BooleanType(false,0); }
	    ( 
	       { e1.type instanceof IntegerType }? -> 
	         integer_noteq_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})
	    |
	       { e1.type instanceof RealType }? ->
	         float_noteq_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})  
	    | 
	       { e1.type instanceof ReferenceType }? ->
	         reference_noteq_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})         
	    | 
	       { e1.type instanceof BooleanType }? ->
	         boolean_noteq_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})         
	    | 
	       { e1.type instanceof CharacterType }? ->
	         character_noteq_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st}) 
	    | 
	       { e1.type instanceof StringType }? ->
	         string_noteq_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})   
	    )
    
	| ^(GR 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = new BooleanType(false,0); }
	     (
	       { e1.type instanceof IntegerType }? ->
	         integer_gr_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})
	     |
	       { e1.type instanceof RealType}? ->
	         float_gr_expression_template(
	           label1 = { LabelGenerator.nextLabel() },
	           label2 = { LabelGenerator.nextLabel() },
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})
	     | 
	       { e1.type instanceof ReferenceType }? 
	       {String classname = ((ReferenceType)e1.type).name; }->
	         reference_gr_expression_template(
			   class_name = {classname},
	           expression1 = {$e1.st}, 
	           expression2 = {$e2.st})
	     )
	  
   | ^(GREQ 
       e1 = expr[environment] 
       e2 = expr[environment])
       
     { retval.type = new IntegerType(false,0); }
     (
       { e1.type instanceof IntegerType }? ->
         integer_greq_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_greq_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})
     | 
       { e1.type instanceof ReferenceType }?
	   {String classname = ((ReferenceType)e1.type).name; }->
	      reference_greq_expression_template(
			class_name = {classname},
            expression1 = {$e1.st}, 
            expression2 = {$e2.st})
     ) 
         
	| ^(LEQ 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = new BooleanType(false,0); }
     (
       { e1.type instanceof IntegerType }? ->
         integer_leq_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_leq_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})
     | 
       { e1.type instanceof ReferenceType }?
	   {String classname = ((ReferenceType)e1.type).name; }->
	     reference_leq_expression_template(
		   class_name = {classname},
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     )
     
	| ^(LS 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = new BooleanType(false,0); }
     (
       { e1.type instanceof IntegerType }? ->
         integer_ls_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_ls_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})
     | 
       { e1.type instanceof ReferenceType }?
	       {String classname = ((ReferenceType)e1.type).name; }->
	         reference_gr_expression_template(
			    class_name = {classname},
           		expression1 = {$e1.st}, 
          		expression2 = {$e2.st})
     )
	
	| ^(AND 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = new BooleanType(false,0); }
	     -> boolean_and_expression_template(
         label1 = { LabelGenerator.nextLabel() },
         label2 = { LabelGenerator.nextLabel() },
         expression1 = {$e1.st}, 
         expression2 = {$e2.st})
	
	| ^(OR 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = new BooleanType(false,0); }
	     -> boolean_or_expression_template(
         label1 = { LabelGenerator.nextLabel() },
         label2 = { LabelGenerator.nextLabel() },
         expression1 = {$e1.st}, 
         expression2 = {$e2.st})
	
	| ^(PLUS 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = e1.type; }
     (
       { e1.type instanceof IntegerType }? ->
         integer_plus_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_plus_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof StringType }? ->
         string_plus_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st},
           expression2 = {$e2.st})
     |
       { e1.type instanceof ReferenceType }? 
       { String cn = ((ReferenceType)e1.type).name;  }		->
         reference_plus_expression_template(
           class_name = {cn},
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     )
     
	| ^(MINUS 
				e1 = expr[environment] 
				e2 = expr[environment])
	
	   { retval.type = e1.type; }
     (
       { e1.type instanceof IntegerType }? ->
         integer_minus_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_minus_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})
     |
       { e1.type instanceof ReferenceType }? 
       { String cn = ((ReferenceType)e1.type).name;  }		->
         reference_minus_expression_template(
           class_name = {cn},
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})     
     
     )
         
	| ^(DIV 
	       e1 = expr[environment] 
	       e2 = expr[environment])
	       
	   { retval.type = e1.type; }
     (
       { e1.type instanceof IntegerType }? ->
         integer_div_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_div_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof ReferenceType }? 
       { String cn = ((ReferenceType)e1.type).name;  }		->
         reference_div_expression_template(
           class_name = {cn},
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     )
	
	| ^(MOD 
	     e1 = expr[environment] 
	     e2 = expr[environment])
  
     { retval.type = e1.type; }
     (
       { e1.type instanceof IntegerType }? ->
         integer_mod_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof ReferenceType }? 
       { String cn = ((ReferenceType)e1.type).name;  }		->
         reference_mod_expression_template(
           class_name = {cn},
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
    )
	
	| ^(MULT 
	     e1 = expr[environment] 
	     e2 = expr[environment])
	     
	   { retval.type = e1.type; }
     (
       { e1.type instanceof IntegerType }? ->
         integer_mult_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
           expression2 = {$e2.st})
     |
       { e1.type instanceof RealType }? ->
         float_mult_expression_template(
           label1 = { LabelGenerator.nextLabel() },
           label2 = { LabelGenerator.nextLabel() },
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})
     |
       { e1.type instanceof ReferenceType }? 
       { String cn = ((ReferenceType)e1.type).name;  }		->
         reference_mult_expression_template(
           class_name = {cn},
           expression1 = {$e1.st}, 
          expression2 = {$e2.st})
     )
     
	| in = INTNUMBER 
	  { retval.type = new IntegerType(false,0); }
	-> int_number_template(number = {$in.text})
	
	| rn = REALNUMBER 
	  { retval.type = new RealType(false,0); }
	-> real_number_template(number = {$rn.text})
	
	| TRUE 
	  { retval.type = new BooleanType(false,0); }
	-> true_template()
	
	| FALSE 
	  { retval.type = new BooleanType(false,0); }
	-> false_template()
	
	| st = STRING 
	  { retval.type = new StringType(false,0); }
	-> string_template(text = {$st.text})
	
	| c = CHAR 
	  { retval.type = new CharacterType(false,0); }
	-> char_template(character = {$c.text.charAt(1)})
	
	| NULL 
	  { retval.type = new ReferenceType(null, false,0); }
	-> null_template()
	
	| ^(VAREXPR 
		 { 
		 	boolean isArr = false; 
		 	String code = ""; 
		 	String type = "";
		 	String type2 = "";
		 	String expr = "";
		 }
	     ei = extended_id
	     (^(INDEX exx = expression[environment]) {isArr = true;} )?)
	     {	     	
	     	String varname = $ei.quantifiers.size() == 0 ? $ei.core : 
	     					 	$ei.quantifiers.get(0);
	     	Variable var = environment.variableEnumerator.getVariable(varname);
	     	type =  var.type == null? "a" :
	     			var.type.isArrayType && !isArr ? "a" :
		            var.type instanceof StringType ? "a" :
		            var.type instanceof IntegerType ? "i" :
		            var.type instanceof RealType ? "f" :
		            var.type instanceof CharacterType ? "i" :
		            var.type instanceof BooleanType ? "i" :
		            var.type instanceof ReferenceType ? "a" : "a";
		            
	     	type2 = var.type == null? "V" :
		            var.type instanceof StringType ? "Ljava/lang/String" :
		            var.type instanceof IntegerType ? "I" :
		            var.type instanceof RealType ? "F" :
		            var.type instanceof CharacterType ? "C" :
		            var.type instanceof BooleanType ? "Z" :
		            var.type instanceof ReferenceType ? 
							"L" + ((ReferenceType)var.type).name + ";" : "V";
		            
		    if(var.type.isArrayType)
		    	type2 = "[" + type2;
		            
	     	if(var.isField) {
				code = "aload_0\n";			  // get (this)
				// load this.varname
				code += "getfield " + environment.currentClass + "/" +
						var.name + " " + type2 +"\n";
				// if we wanted to read value from table
				// (now the reference for table is on stack)
				if(var.type.isArrayType && isArr) {
					code += $exx.st + "\n";	  // push index to table
					code += type + "aload\n"; // get value from this field
				}
	     	}
	     	else {
	     		if(var.type.isArrayType && isArr) {
	     			code = "aload " + var.number + "\n";
	     			code += $exx.st;
	     			code += type + "aload\n";
	     		}
	     		else {
	     			code = type + "load " + var.number + "\n";
	     		}
	     	}
	     	
	     	// we change isArrayType to false in var.type if there was 
	     	// an index reference to array
	     	if(isArr)
	     		retval.type = var.type.copy();
	     	else
	     		retval.type = var.type;					
	     }
	->varexpr_template(code={code})
      	 
	| ^(NOT 
	     e = expr[environment])
	     
	  { retval.type = new BooleanType(false,0); } 
	  
	 -> boolean_not_expression_template(expression = {$e.st})
	
	| ^(CAST 
	     e = expr[environment]
	     s = simple_type)   
	  { 
	    retval.type = $s.type; 
	      	   
	    if ($e.type instanceof IntegerType) {
	      if ($s.type instanceof IntegerType) {
	        retval.st = new StringTemplate($e.st.toString());
	        
		    } else
		    if ($s.type instanceof RealType) {
		      // integer to float
		      retval.st = new StringTemplate(
		        $e.st.toString() + "\n" + "i2f" +"\n");
		        
		    } else
		    if ($s.type instanceof CharacterType) {
		      // integer to char
	        retval.st = new StringTemplate(
	          $e.st.toString() + "\n" + "i2c" +"\n");
	          
		    } else
		    if ($s.type instanceof BooleanType) {
		      // integer to boolean
	        retval.st = new StringTemplate($e.st.toString());
	        
		    } else
		    if ($s.type instanceof StringType) {
		      // integer to string
	        retval.st = new StringTemplate(
	         $e.st.toString() + "\ninvokestatic " +
	         "java/lang/String/valueOf(I)Ljava/lang/String;\n");
	         
		    }
	    } else
	    if ($e.type instanceof RealType) {
	      if ($s.type instanceof IntegerType) {    
	        // float to integer
	        retval.st = new StringTemplate(
	          $e.st.toString() + "\n" + "f2i" +"\n");
	     
		    } else
		    if ($s.type instanceof RealType) { 
		      // float to float
		      retval.st = $e.st;
		      
		    } else
		    if ($s.type instanceof StringType) {
		      // float to string
		      retval.st = new StringTemplate(
	         $e.st.toString() + "\ninvokestatic " +
	         "java/lang/String/valueOf(F)Ljava/lang/String;\n");
	         
		    }
	    } else
	    if ($e.type instanceof CharacterType) {
	      if ($s.type instanceof IntegerType) {
	        retval.st = $e.st;
	        
		    } else
		    if ($s.type instanceof CharacterType) {
		      retval.st = $e.st;
		      
		    } else
		    if ($s.type instanceof BooleanType) {
		      retval.st = $e.st;
		      
		    } else
		    if ($s.type instanceof StringType) {
		      retval.st = new StringTemplate(
	         $e.st.toString() + "\ninvokestatic " +
	         "java/lang/String/valueOf(C)Ljava/lang/String;\n");
	         
		    }
	    } else
	    if ($e.type instanceof BooleanType) {
	      if ($s.type instanceof IntegerType) {
	        retval.st = $e.st;
	        
		    } else
		    if ($s.type instanceof RealType) {
		      retval.st = new StringTemplate(
		        $e.st + "\n" + "i2f" + "\n");
		        
		    } else
		    if ($s.type instanceof BooleanType) {
		      retval.st = $e.st;
		      
		    } else
		    if ($s.type instanceof StringType) {
		      retval.st = new StringTemplate(
	         $e.st.toString() + "\ninvokestatic " +
	         "java/lang/String/valueOf(Z)Ljava/lang/String;\n");
		    }
	    } else
	    if ($e.type instanceof StringType) {
		    if ($s.type instanceof StringType) {
		      retval.st = $e.st;
		      
		    }
	    }
		}	
	| t=call[environment]
	  {
	  	retval.st = $t.st;
	  	retval.type = $t.type;
	  }
	
	;	
	
boolean_operator returns [String name]
	: AND { retval.name = OP_AND_NAME; }
	| OR  { retval.name = OP_OR_NAME; }
	;
	
relational_operator returns [String name]
	: EQUAL { retval.name = OP_EQUAL_NAME; }
	| NOTEQ { retval.name = OP_NOTEQ_NAME; }
  | GR    { retval.name = OP_GREATER_NAME; }
  | GREQ  { retval.name = OP_GREATER_EQUAL_NAME; }
  | LS    { retval.name = OP_LESS_NAME; }
  | LEQ   { retval.name = OP_LESS_EQUAL_NAME; }
	;

additive_operator returns [String name]
	: PLUS  { retval.name = OP_PLUS_NAME; }
	| MINUS { retval.name = OP_MINUS_NAME; }
	;

multiplicative_operator returns [String name]
	: DIV { retval.name = OP_DIV_NAME; }
	| MOD { retval.name = OP_MOD_NAME; }
	| MULT { retval.name = OP_MULT_NAME; }
	;	

operator
	: relational_operator 
	| additive_operator
	| multiplicative_operator
	| boolean_operator
	;